﻿using System;
using System.Linq;
using System.Web.Caching;
using Microsoft.Practices.ServiceLocation;
using Pap.Core.CronJobs;
using Pap.Data;
using Pap.Data.Abstraction.Utils;
using Pap.Services;
using Pap.Services.Vactions;
using Pap.Services.WorkflowEngine;
using Utils;

namespace Pap.Web.Utils.CronJobs
{
    /// <summary>
    /// 
    /// </summary>
    public class PenaltiesCronJob : ICronJob
    {
        /// <summary>
        /// 
        /// </summary>
        IWorkflowService _WfService;
        /// <summary>
        /// 
        /// </summary>
        ILoanRepository _LoanRepository;
        /// <summary>
        /// 
        /// </summary>
        IVacationService _VacationService;

        /// <summary>
        /// Initializes a new instance of the <see cref="PenaltiesCronJob"/> class.
        /// </summary>
        /// <param name="wfService">The wf service.</param>
        /// <param name="loanRepository">The loan repository.</param>
        /// <param name="vacationService">The vacation service.</param>
        public PenaltiesCronJob(IWorkflowService wfService, ILoanRepository loanRepository, IVacationService vacationService)
        {
            _WfService = wfService;
            _LoanRepository = loanRepository;
            _VacationService = vacationService;
        }

        #region ICronJob Members

        /// <summary>
        /// Gets the sliding time.
        /// </summary>
        public TimeSpan SlidingTime
        {
            get { return TimeSpan.FromDays(1); }
        }

        /// <summary>
        /// Gets the name of the job.
        /// </summary>
        /// <value>
        /// The name of the job.
        /// </value>
        public string JobName
        {
            get { return "Caluclate Penalties"; }
        }

        /// <summary>
        /// Executes this instance.
        /// </summary>
        public void Execute()
        {
            using (IUnitOfWork trans = ServiceLocator.Current.GetInstance<IUnitOfWork>())
            {
                var paymentLoansInstance = _WfService.GetInStep(Steps.PaymentList);
                var paymentLoansExecutions = _LoanRepository.FindCustomerLoans(loan => paymentLoansInstance.Contains(
                    loan.LoanID)).Where(loan => loan.LoanExecutionLst.Count() > 0).Select(loan => loan.LoanExecutionLst.First());

                paymentLoansExecutions.ForEach(loanEx =>
                    {
                        var lateInstallments = from inst in loanEx.LoanExecutionInstallmentsLst.Where(ins => !ins.IsPaid)
                                               let delay = (DateTime.Today.Date - inst.InstallmentDate.Date).Days
                                               where delay > 0 && delay <= DateTime.DaysInMonth(inst.InstallmentDate.Year, inst.InstallmentDate.Month)
                                               select inst;
                        var lastInst = lateInstallments.OrderBy(inst => inst.InstallmentDate).FirstOrDefault();
                        lateInstallments.ForEach(loanInst =>
                            {
                                int vacDays = _VacationService.getAllvacationDays(loanInst.InstallmentDate, DateTime.Today);
                                loanInst.PenaltyDays = (DateTime.Today.Date - loanInst.InstallmentDate.Date).Days - vacDays;

                                if (loanEx.LoanPaymentMethodID.Equals(Enums.ToString(PaymentMethods.Periodical).ToGuid()))
                                {
                                    loanInst.PenaltyAmount = (loanInst.OriginalInstallmentAmount * loanInst.PenaltyDays) / 100;
                                }
                                else
                                    if (loanEx.LoanPaymentMethodID.Equals(Enums.ToString(PaymentMethods.GracePeriod).ToGuid()))
                                    {
                                        if (loanInst.LoanExecutionInstallmentID.Equals(lastInst.LoanExecutionInstallmentID))
                                            loanInst.PenaltyAmount = loanInst.OriginalInstallmentAmount * loanInst.PenaltyDays * .25 / 100;
                                        else
                                            loanInst.PenaltyAmount = loanInst.OriginalInstallmentAmount * loanInst.PenaltyDays * 10 / 100;
                                    }

                                if (loanInst.PenaltyDays > 0 && !HasPrevWarnings(loanInst.LoanExecutionInstallmentID))
                                {
                                    ILoanInstallmentWarning instWarning = ServiceLocator.Current.GetInstance<ILoanInstallmentWarning>();
                                    instWarning.LoanIntstallmentWarningID = Guid.NewGuid();
                                    instWarning.LoanExecutionInstallmentID = loanInst.LoanExecutionInstallmentID;
                                    instWarning.WarningDate = DateTime.Now;
                                    _LoanRepository.CreateLoanInstallmentWarning(instWarning);
                                }

                            }
                            );
                    });
                trans.Commit();
            }
        }

        /// <summary>
        /// Determines whether [has prev warnings] [the specified loan installment ID].
        /// </summary>
        /// <param name="loanInstallmentID">The loan installment ID.</param>
        /// <returns>
        ///   <c>true</c> if [has prev warnings] [the specified loan installment ID]; otherwise, <c>false</c>.
        /// </returns>
        private bool HasPrevWarnings(Guid loanInstallmentID)
        {
            var warnings = _LoanRepository.FindLoanInstallmentWarnings(warning => warning.LoanExecutionInstallmentID.Equals(loanInstallmentID));
            return warnings.Count() > 0;
        }

        #endregion
    }
}
